package service

import (
	"context"
	"fmt"
	"iris-wms/apps/admin/web/dto/din"
	"iris-wms/apps/admin/web/dto/dout"
	"iris-wms/common/model"
	"iris-wms/common/pkg/db"
	"iris-wms/common/pkg/db/dao"
	"iris-wms/common/pkg/icasbin"
	"strconv"

	"github.com/kataras/iris/v12"
	"gorm.io/gen"
	"gorm.io/gorm"
)

var _ AdminRoleService = (*adminRoleService)(nil)

type AdminRoleService interface {
	Create(model.AdminRole) error
	Update(model.AdminRole) error
	DeleteByIdList([]uint) error
	GetTable(din.AdminRoleTableIn) ([]*dout.AdminRoleOut, int64, error)
	GetList() ([]*model.AdminRole, error)
	Grant(din.AdminRoleGrant) error
	Owned(uint) (iris.Map, error)
}

func NewAdminRoleService() *adminRoleService {
	return &adminRoleService{}
}

type adminRoleService struct{}

func (impl *adminRoleService) Create(in model.AdminRole) (err error) {
	err = db.GDB1.Create(&in).Error
	return
}

func (impl *adminRoleService) Update(in model.AdminRole) (err error) {
	err = db.GDB1.Save(&in).Error
	return
}

func (impl *adminRoleService) DeleteByIdList(idList []uint) (err error) {
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Delete(&model.AdminRole{}, "id in ?", idList).Error; err != nil {
			return err
		}
		if err = tx.Delete(&model.AdminRoleMenu{}, "rid in ?", idList).Error; err != nil {
			return err
		}
		if err = tx.Delete(&model.AdminRolePolicy{}, "rid in ?", idList).Error; err != nil {
			return err
		}
		if err = tx.Delete(&model.AdminRule{},
			"(ptype='g' AND v1 in ?) OR (ptype='p' AND v0 in ?)", idList, idList).Error; err != nil {
			return err
		}
		return nil
	})
	return
}

func (impl *adminRoleService) GetTable(in din.AdminRoleTableIn) (rows []*dout.AdminRoleOut, total int64, err error) {
	q := dao.Use(db.GDB1).AdminRoleOut
	do := q.WithContext(context.Background())

	condList := make([]gen.Condition, 0)
	if in.Enable > 0 {
		condList = append(condList, q.Enable.Eq(in.Enable))
	}
	if in.Word != "" {
		condList = append(condList, q.Name.Like(fmt.Sprintf("%%%s%%", in.Word)))
	}
	rows, total, err = do.Where(condList...).
		Preload(q.Creator).
		FindByPage(in.Offset(), in.PageSize)
	return
}

func (impl *adminRoleService) GetList() (rows []*model.AdminRole, err error) {
	err = db.GDB1.Find(&rows).Error
	return
}

func (impl *adminRoleService) Grant(in din.AdminRoleGrant) (err error) {
	var (
		policyList = make([]*model.AdminPolicy, 0)
		m1         = make([]*model.AdminRule, 0)
		m2         = make([]*model.AdminRoleMenu, 0)
	)
	if err = db.GDB1.Find(&policyList, "id in ?", in.PidList).Error; err != nil {
		return
	}
	if err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Delete(&model.AdminRule{}, "ptype='p' AND v0 = ?", in.Rid).Error; err != nil {
			return err
		}
		if err = tx.Delete(&model.AdminRolePolicy{}, "rid = ?", in.Rid).Error; err != nil {
			return err
		}
		if err = tx.Delete(&model.AdminRoleMenu{}, "rid = ?", in.Rid).Error; err != nil {
			return err
		}
		//
		if in.Rid > 0 && len(policyList) > 0 {
			for _, v := range policyList {
				m1 = append(m1, &model.AdminRule{
					Ptype: "p",
					V0:    strconv.FormatUint(uint64(in.Rid), 10),
					V1:    v.V1,
					V2:    v.V2,
					V3:    v.V3,
					V4:    v.V4,
					V5:    v.V5,
				})
			}
		}
		if err = tx.CreateInBatches(m1, 100).Error; err != nil {
			return err
		}
		for _, v := range in.PidList {
			if err = tx.Create(&model.AdminRolePolicy{
				Rid: uint(in.Rid),
				Pid: uint(v),
			}).Error; err != nil {
				return err
			}
		}

		if in.Rid > 0 && len(in.MidList) > 0 {
			for _, v := range in.MidList {
				if len(v.Bid) > 0 {
					for _, vv := range v.Bid {
						m2 = append(m2, &model.AdminRoleMenu{
							Rid: in.Rid,
							Mid: v.ID,
							Bid: vv,
						})
					}
				} else {
					m2 = append(m2, &model.AdminRoleMenu{Rid: in.Rid, Mid: v.ID})
				}
			}
		}
		if err = tx.CreateInBatches(m2, 100).Error; err != nil {
			return err
		}
		return nil
	}); err != nil {
		return
	}
	if err = icasbin.GCasbin.LoadPolicy(); err != nil {
		return
	}
	return
}

func (impl *adminRoleService) Owned(rid uint) (iris.Map, error) {
	var (
		err        error
		menuList   = make([]int64, 0)
		policyList = make([]int64, 0)
	)
	if err = db.GDB1.Model(&model.AdminRoleMenu{}).Select("distinct mid").
		Where("rid=?", rid).
		Scan(&menuList).Error; err != nil {
		return nil, err
	}
	if err = db.GDB1.Model(&model.AdminRolePolicy{}).Select("distinct pid").
		Where("rid=?", rid).
		Scan(&policyList).Error; err != nil {
		return nil, err
	}
	return iris.Map{
		"menuList":   menuList,
		"policyList": policyList,
	}, nil
}
